package com.ruoyi.game.core;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.game.constant.GameErrorCode;
import com.ruoyi.game.constant.GameRoomProperties;
import com.ruoyi.game.core.card.BasePoker;
import com.ruoyi.game.core.card.BasePokerLogic;
import com.ruoyi.game.core.exception.GameOpException;
import com.ruoyi.game.core.status.LiangZhuInfo;
import com.ruoyi.game.domain.GamePlayer;
import com.ruoyi.game.domain.GameSet;
import com.ruoyi.game.service.IGamePlayerService;
import com.ruoyi.game.service.IGameSetService;
import com.ruoyi.game.websocket.handler.GameWebSocketHandler;
import com.ruoyi.game.websocket.message.packet.*;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.WebSocketSession;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import static com.ruoyi.game.core.card.BasePoker.toCardString;

@Slf4j
@Data
@RequiredArgsConstructor
public class GameSetCore {

    private final GameWebSocketHandler socketHandler;      // WebSocket 通信句柄
    private final GameRoomProperties gameRoomProperties;
    private final long setId;                              // 当前对局局号
    private final GameRoomCore room;                           // 所属房间对象
    private final Map<Integer, GameSetPos> playerMap = new HashMap<>(); // 座位号 → 玩家位置对象映射
    private final IGameSetService gameSetService;    // 注入 IGameSetService
    private final IGamePlayerService gamePlayerService;    // 注入 IGamePlayerService
    private List<Integer> cardDeck = new ArrayList<>();           // 当前牌堆
    private List<Integer> remainningDeck = new ArrayList<>();           // 除去底牌的当前牌堆
    private List<Integer> kouPaiList = new ArrayList<>();  // 当前局庄家扣底的底牌

    private int dealerSeatIndex = -1;      // 庄家座位号
    private SetState state = SetState.CALL_ZHU_STATE; // 当前对局状态（发牌、抢庄、出牌、结算等）
    private int roundId = 0;               // 回合编号（每打出一轮递增）
    private int zhuValue = 2;             // 主值永远为2
    private Date endTime = null;


    private GameRoundCore currentRound;        // 当前出牌回合对象
    private int hanDaCard = -1;            // 当前局喊打使用的卡牌（只允许一张）
    private boolean oneVsFour = false;     // 是否为打独（1打4）模式
    private final Set<Integer> alreadyOpSeatSet = new HashSet<>(); // 抢庄、亮主等阶段中已操作的座位号
    private int latestWinnerSeat = -1;        // 上回合最大赢家座位号

    private long grabStartTime = System.currentTimeMillis() / 1000; // 抢庄开始时间（秒）

    private final Map<Integer, Set<Integer>> teamMap = new HashMap<>(); // 队伍划分：队伍号 → 玩家座位集合
    private int handaPos = -1;              // 当前局喊打的帮手位置（若有）

    private LiangZhuInfo liangZhuInfo;      // 最新一次亮主信息
    private boolean isFanzhu = false;       // 是否发生了反主
    private int baseScoreMultiplier = 1;    // 当前底分倍数（默认 1，反主、自反等可加倍）
    private int curK510Point = 0;           // 当前局累计的 510K 分数（含吃牌和底牌）
    private boolean playStartNotified = false;

    private int shengjiCount = 0;           // 当前局升级层数（0=不升，1=升一级...）
    private int dipaiStartSec = -1;        // 当前局抠底牌开始时间（秒）
    private int hanDaStartSec = -1;        // 当前局喊打开始时间（秒）
    private List<Integer> prevRoundCards = new ArrayList<>();       //保存上一轮出牌
    private long roundEndWaitUntil = -1; // -1表示无等待；否则是需要等待到的时间戳（单位秒）
    private int lastPushedShengjiMultiplier = -999; // 初始化为不可能的值
    private int fandiScore = 0;             // 翻底的加分

    public GameSetCore(long setId, GameRoomCore room, IGameSetService gameSetService, IGamePlayerService gamePlayerService, GameRoomProperties gameRoomProperties) {
        this.setId = setId;
        this.room = room;
        this.socketHandler = room.getSocketHandler();
        this.gameSetService = gameSetService;
        this.gamePlayerService = gamePlayerService;
        this.gameRoomProperties = gameRoomProperties;
        initPlayers();
        log.info("[GameSetCore] 初始化新对局 setId={}, roomId={}", setId, room.getRoomId());
    }

    public boolean isZhuangWin() {
        return curK510Point < 80;
    }

    public void setKouPaiList(List<Integer> cards) {
        this.kouPaiList = new ArrayList<>(cards);
    }
    public void setHanDaCard(int card) {
        this.hanDaCard = card;

        // 设置一打多模式为 false
        this.setOneVsFour(false);

        // 更新队伍信息
        this.updateTeamByHanDa(card);

        // ✅ 广播喊打帮手信息
        int zhuang = this.getDealerSeatIndex();
        int helper = this.getHandaPos();
        if (helper != -1) {
            WsPacket<NotifyHanDaHelperPush> packet = WsPackets.notifyHanDaHelper(room.getRoomId(), helper, zhuang);
            socketHandler.broadcastPacketToRoom(room.getRoomId(), packet);
            log.info("[GameSetCore:{}] 广播喊打帮手信息，庄家={}，帮手={}", setId, zhuang, helper);
        }
    }
    public void setOneVsFour(boolean val) {
        this.oneVsFour = val;
    }

    public boolean isOneVsFour() {
        return oneVsFour;
    }

    public int getHandaPos() {
        return handaPos;
    }

    /**
     * 玩家亮主
     * @param cards    亮主牌（一般为 1 或 2 或 3 张）
     * @param opType   操作类型（如 LIANG_ZHU1 / FAN_ZHU 等）
     * @param seat     亮主玩家位置
     */
    public void callZhu(List<Integer> cards, PKOpType opType, int seat) {
        String cardDescriptions = cards.stream()
                .map(BasePoker::getCardDescription)
                .collect(Collectors.joining(", "));

        log.info("[GameSetCore] 玩家 seat={} 亮主，opType={}，cards={}", seat, opType, cardDescriptions);

        GameSetPos pos = playerMap.get(seat);
        if (pos == null) {
            log.error("[callZhu] seat={} 找不到玩家", seat);
            GameRoomPos roomPos = room.getSeats().get(seat);
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
            if (roomPos != null && session != null) {
                socketHandler.sendError(session, GameErrorCode.PLAYER_NOT_FOUND, "亮主失败，找不到玩家");
            }
            throw new GameOpException(GameErrorCode.PLAYER_NOT_FOUND, "找不到玩家");
        }

        int mainCardColor = -1;
        // 如果亮主是3张牌，确保其中有花色的主牌来设置主花色
        try {
            mainCardColor = BasePoker.calcMainColor(cards, getZhuValue());
            log.info("[GameSetCore] 主花色计算完成：{}", BasePoker.PockerColorType.valueOf(mainCardColor));
        } catch (Exception e) {
            log.error("[callZhu] 亮主牌异常: {}", e.getMessage(), e);
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
            if (session != null) {
                socketHandler.sendError(session, GameErrorCode.LIANGZHU_ERROR, "亮主失败，牌错误");
            }

            throw new GameOpException(GameErrorCode.INVALID_CARD, "亮主牌错误！");
        }

        if (opType == PKOpType.ZI_BAO && liangZhuInfo != null && liangZhuInfo.getSeatIndex() == seat) {
            // 自保：在原有亮主基础上追加 zhuCards
            List<Integer> newCards = new ArrayList<>(liangZhuInfo.getZhuCards());
            newCards.addAll(cards);
            liangZhuInfo.setZhuCards(newCards);
            liangZhuInfo.setOpType(opType); // 自保会覆盖操作类型为 ZI_BAO（也可以不改）
            log.info("[GameSetCore] 自保追加主牌，seat={}，新主牌={}", seat, BasePoker.toCardString(newCards));
        } else {
            // 非自保或首次亮主：正常设置
            LiangZhuInfo info = new LiangZhuInfo(seat, opType, new ArrayList<>(cards), BasePoker.getCardValueEx(cards.get(0)), mainCardColor);
            liangZhuInfo = info;
            log.info("[GameSetCore] 设置新亮主信息，seat={}，主牌={}", seat, BasePoker.toCardString(cards));
        }

        setDealerSeatIndex(seat);
        log.info("[GameSetCore] 设置庄家，seat={}", seat);

        // 将已亮的主牌添加到玩家的 zhuCards 列表中
        pos = playerMap.get(seat);
        if (pos != null) {
            pos.setLiangZhuInfo(liangZhuInfo);
            log.info("[GameSetCore] 更新玩家 seat={} 的已亮主牌：{}", seat, cards);
        }

        // 如果是反主，更新底分或其他逻辑（如倍数）
        if (opType == PKOpType.FAN_ZHU || opType == PKOpType.ZI_FAN) {
            isFanzhu = true;
            updateBaseScore();
            log.info("[GameSetCore] 反主操作，更新底分，当前倍数：{}", baseScoreMultiplier);
        }

        // 广播亮主消息
        notifyLiangZhuInfo(seat, opType, cards);
        log.info("[GameSetCore] 广播亮主消息，玩家 seat={}，opType={}，cards={}", seat, opType, cardDescriptions);
    }

    public void updateBaseScore() {
        if (baseScoreMultiplier < 2) {
            baseScoreMultiplier *= 2;
            log.info("[GameSetCore] 反主/自反，加倍底分，现在倍数为：{}", baseScoreMultiplier);
        } else {
            log.info("[GameSetCore] 当前已达最大倍数，忽略加倍");
        }
    }

    private void notifyKouDiStart() {
        int dealerSeatIndex = getDealerSeatIndex();
        GameRoomPos roomPos = room.getSeats().get(dealerSeatIndex);
        WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
        if (session != null && session.isOpen()) {
            socketHandler.sendPacketToSession(session,
                    WsPackets.kouDiStart(currentRound.getRoundId(), gameRoomProperties.getKouDiTimeLimit()));
        }
    }

    public void notifyKouDiResult() {
        int dealerSeatIndex = getDealerSeatIndex();
        GameRoomPos roomPos = room.getSeats().get(dealerSeatIndex);
        WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
        if (session != null && session.isOpen()) {
            socketHandler.sendPacketToSession(session, WsPackets.kouDiResult(kouPaiList));
        }
    }

    private void notifyHandaWait() {
        int dealerSeat = getDealerSeatIndex();
        WsPacket<?> packet = WsPackets.handaStart(dealerSeat);

        for (GameRoomPos pos : room.getSeats()) {
            if (pos.isEmpty()) continue;

            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
            if (session != null && session.isOpen()) {
                socketHandler.sendPacketToSession(session, packet);
            }
        }

        log.info("[GameSetCore] 广播喊打等待封包，seat={}", dealerSeat);
    }

    public void notifyHandaResult() {
        Set<Integer> dealerTeam = teamMap.getOrDefault(1, Collections.emptySet());
        Set<Integer> idleTeam = teamMap.getOrDefault(2, Collections.emptySet());

        WsPacket<?> packet = WsPackets.handaResult(
                new ArrayList<>(dealerTeam),
                new ArrayList<>(idleTeam),
                handaPos // 👉 可以为空或 -1，前端判断是否有帮手
        );

        for (GameRoomPos pos : room.getSeats()) {
            if (pos.isEmpty()) continue;
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
            if (session != null && session.isOpen()) {
                socketHandler.sendPacketToSession(session, packet);
            }
        }

        log.info("[GameSetCore] 广播喊打结果封包：庄家队伍={}，闲家队伍={}，喊打帮手={}",
                dealerTeam, idleTeam, handaPos == -1 ? "无" : handaPos);
    }

    private void notifyLiangZhuInfo(int seat, PKOpType opType, List<Integer> cards) {
        // 将 cards 转换为易读的牌面描述
        String cardDescriptions = cards.stream()
                .map(BasePoker::getCardDescription)
                .collect(Collectors.joining(", "));

        log.info("[GameSetCore] 通知所有玩家，seat={} 亮主，类型={}，牌={}", seat, opType, cardDescriptions);

        for (GameRoomPos pos : room.getSeats()) {
            if (pos.isEmpty()) continue;
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
            if (session != null && session.isOpen()) {
                WsPacket<?> packet = WsPackets.liangZhuInfo(setId, seat, mapToReq(opType), cards);
                socketHandler.sendPacketToSession(session, packet);
            }
        }
    }

    public void updateTeamByHanDa(int card) {
        int dPos = this.getDealerSeatIndex(); // 当前庄家位置
        int selectedValue = BasePoker.getCardValue(card);
        int selectedColor = BasePoker.getCardColor(card);

        log.info("[GameSetCore] 喊打更新队伍 - 庄家pos={}, 喊打牌: {} (花色={}, 点数={})", dPos, card, selectedColor, selectedValue);

        teamMap.clear();
        Set<Integer> team1 = new HashSet<>(); // 包含庄家及拥有该牌的玩家
        Set<Integer> team2 = new HashSet<>();

        team1.add(dPos);

        for (Map.Entry<Integer, GameSetPos> entry : playerMap.entrySet()) {
            int pos = entry.getKey();
            if (pos == dPos) continue;

            List<Integer> handCards = entry.getValue().getHandCards();
            boolean hasSameCard = handCards.stream()
                    .anyMatch(c -> BasePoker.getCardValue(c) == selectedValue &&
                            BasePoker.getCardColor(c) == selectedColor);

            if (hasSameCard) {
                team1.add(pos);
            } else {
                team2.add(pos);
            }
        }

        teamMap.put(1, team1);
        teamMap.put(2, team2);

        // 查找除庄家以外的第一个队友作为喊打帮手
        handaPos = -1;
        for (Integer pos : team1) {
            if (pos != dPos) {
                handaPos = pos;
                break;
            }
        }

        log.info("[GameSetCore] 队伍分配完成：队伍1 = {}，队伍2 = {}，喊打帮手 = {}", team1, team2, handaPos == -1 ? "无" : handaPos);
    }

    public void updateTeamToOneVsFour() {
        int dPos = this.getDealerSeatIndex();
        log.info("[GameSetCore] 打独模式启动，庄家位置：{}", dPos);

        teamMap.clear();

        Set<Integer> team1 = new HashSet<>();
        team1.add(dPos);

        Set<Integer> team2 = new HashSet<>();
        for (Integer seatIndex : playerMap.keySet()) {
            if (seatIndex != dPos) {
                team2.add(seatIndex);
            }
        }

        teamMap.put(1, team1);
        teamMap.put(2, team2);

        handaPos = -1; // 打独无帮手
        log.info("[GameSetCore] 打独模式完成 - 队伍1: {}（庄家），队伍2: {}", team1, team2);
    }

    public int getTeamId(int seatIndex) {
        return teamMap.entrySet().stream()
                .filter(e -> e.getValue().contains(seatIndex))
                .map(Map.Entry::getKey)
                .findFirst().orElse(-1);
    }

    public boolean isSameTeam(int a, int b) {
        return getTeamId(a) != -1 && getTeamId(a) == getTeamId(b);
    }

    private void initPlayers() {
        for (GameRoomPos pos : room.getSeats()) {
            if (!pos.isEmpty()) {
                GameSetPos setPos = new GameSetPos(pos.getPlayerId(), pos.getSeatIndex(), this);
                setPos.setGameSet(this);
                playerMap.put(pos.getSeatIndex(), setPos);
            }
        }
        log.info("[GameSetCore:{}] 初始化玩家列表: {}", setId, playerMap.keySet());
    }

    private void shuffleAndDeal() {
        ArrayList<Integer> fullDeck = null;
        if (gameRoomProperties.isRandomCard()) {
            fullDeck = BasePokerLogic.getOnlyRandomPockerList(2, 2, BasePoker.PockerListType.POCKERLISTTYPE_AEND);
        }
        else {
            fullDeck = BasePokerLogic.getFixedPokerList();
        }

        int playerNum = playerMap.size();
        if (playerNum == 0) {
            log.error("[shuffleAndDeal] 无可发牌的玩家！");
            for (GameRoomPos pos : room.getSeats()) {
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                if (!pos.isEmpty() && session != null) {
                    socketHandler.sendError(session, GameErrorCode.NO_PLAYER_TO_DEAL, "房间没有可发牌的玩家，无法开始游戏");
                }
            }
            throw new GameOpException(GameErrorCode.NO_PLAYER_TO_DEAL, "无可发牌的玩家!");
        }

        // 均匀分发到每个玩家
        log.info("[GameSetCore] 洗牌完成，牌堆：{}", toCardString(fullDeck));

        // 均匀分发到每个玩家之前，先将8张底牌分离出来
        kouPaiList = fullDeck.subList(fullDeck.size() - 8, fullDeck.size());
        List<Integer> remainingDeck = fullDeck.subList(0, fullDeck.size() - 8);

        // 记录底牌
        log.info("[GameSetCore] 底牌：{}", toCardString(kouPaiList));

        for (int i = 0; i < remainingDeck.size(); i++) {
            int seat = i % playerNum;
            List<Integer> handCards = playerMap.get(seat).getHandCards();
            handCards.add(remainingDeck.get(i));

            // 记录发牌信息，将牌的花色和牌值打印成易读的格式
            log.info("[GameSetCore] 发牌：seat={}，card={}", seat, toCardString(remainingDeck.get(i)));
        }

        log.info("[GameRoomSet] 发牌完成，共 {} 张牌，玩家人数 = {}，庄家 = seatIndex {}", remainingDeck.size(), playerNum, dealerSeatIndex);
        // 打印每个玩家的手牌
        for (Map.Entry<Integer, GameSetPos> entry : playerMap.entrySet()) {
            int seatIndex = entry.getKey();
            GameSetPos pos = entry.getValue();
            log.info("[GameSetCore] 玩家{}手牌：{}", seatIndex, toCardString(pos.getHandCards()));
        }

        Map<Integer, List<Integer>> handCardsMap = new HashMap<>();
        for (Map.Entry<Integer, GameSetPos> entry : playerMap.entrySet()) {
            handCardsMap.put(entry.getKey(), new ArrayList<>(entry.getValue().getHandCards()));
        }
        // 均匀发完牌后
        this.remainningDeck = remainingDeck;

        // 推送底牌和玩家的发牌信息
        WsPacket<StartSetPush> packet = WsPackets.startSet(
                (int) setId,
                dealerSeatIndex,
                handCardsMap,
                kouPaiList,
                new ArrayList<>(fullDeck),
                gameRoomProperties.getCallzhuTimeLimit()
        );

        // 推送
        socketHandler.broadcastPacketToRoom(
                room.getRoomId(),
                packet
        );
    }

    public boolean update(int sec) {
        log.info("[GameSetCore] 当前状态 = {}, sec = {}", this.state, sec);
        GameRoundCore round =null;
        GameRoundPos roundPos = null;

        long now = System.currentTimeMillis() / 1000;
        if (roundEndWaitUntil > 0) {
            if (now < roundEndWaitUntil) {
                // 正在等待动画/阶段切换，不处理本帧逻辑
                return false;
            }

            // 等待时间已到，处理对应逻辑
            roundEndWaitUntil = -1;

            if (state == SetState.KOU_DI_STATE) {
                this.state = SetState.HAN_DA_STATE;
                currentRound.endRound();
                currentRound = null;
                log.info("[GameSetCore] 进入喊打阶段");
                return true;
            }

            if (state == SetState.HAN_DA_STATE) {
                this.state = SetState.PLAYING_STATE;
                currentRound.endRound();
                currentRound = null;
                log.info("[GameSetCore] 进入出牌阶段");
                return true;
            }

            if (state == SetState.PLAYING_STATE && currentRound != null) {
                currentRound.endRound();
                if (isAllCardsPlayed()) {
                    room.endSet();
                    log.info("[GameSetCore:{}] 所有玩家手牌为空，标记局结束", setId);
                }
                currentRound = null;
                log.info("[GameSetCore] 出牌回合结算完成，等待下一回合");
                return true;
            }
        }

        switch (this.state) {
            case CALL_ZHU_STATE:
                if (currentRound == null) {
                    try {
                        this.shuffleAndDeal();
                    } catch (Exception e) {
                        log.error("[GameSetCore] 洗牌发牌失败", e);
                        room.getSeats().forEach(pos -> {
                            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                            if (!pos.isEmpty() && session != null) {
                                socketHandler.sendError(session, GameErrorCode.DEAL_ERROR, "发牌失败");
                            }
                        });
                        throw new GameOpException(GameErrorCode.DEAL_ERROR, "洗牌发牌失败!");
                    }

                    round = new GameRoundCore(this, ++roundId);
                    for (Integer seatIndex : playerMap.keySet()) {
                        GameRoundPos pos = new GameRoundPos(seatIndex);
                        pos.getReceiveOpTypes().addAll(List.of(
                                PKOpType.LIANG_ZHU1, PKOpType.LIANG_ZHU2,
                                PKOpType.FAN_ZHU, PKOpType.ZI_BAO, PKOpType.ZI_FAN
                        ));
                        round.getRoundPosMap().put(seatIndex, pos);
                    }
                    this.currentRound = round;
                    notifyRoundStart();
                    log.info("[GameSetCore] 发牌并进入抢庄阶段，可亮主等");
                }

                if (isGrabZhuangEnd(sec)) {
                    log.info("[GameSetCore] 抢庄结束，进入翻底牌阶段");
                    if (!anyoneCalledMaster()) {
                        log.info("[GameSetCore] 无人抢庄，进入摸老底");
                    }
                    dealDiPai();

                    // 打印庄家和主的颜色和牌型
                    int dealerSeatIndex = getDealerSeatIndex();
                    GameSetPos dealerPos = getGameSetPosBySeatIndex(dealerSeatIndex);
                    if (dealerPos != null) {
                        log.info("[########GameSetCore########] 庄家: 玩家 {}, 主花色: {}, 主牌型: {}",
                                dealerPos.getPlayerId(), liangZhuInfo.getZhuColor(), liangZhuInfo.getZhuValue());
                    } else {
                        log.warn("[GameSetCore] 无法找到庄家信息");
                    }
                    this.state = SetState.KOU_DI_STATE;

                    notifyCallZhuEnd();
                    return true;
                }
                break;
            case KOU_DI_STATE:
                if (dipaiStartSec == -1) {
                    dipaiStartSec = sec;
                    round = new GameRoundCore(this, ++roundId);
                    roundPos = new GameRoundPos(getDealerSeatIndex());
                    roundPos.getReceiveOpTypes().clear();
                    roundPos.getReceiveOpTypes().add(PKOpType.KOU_DI);
                    round.getRoundPosMap().put(roundPos.getSeatIndex(), roundPos);
                    this.currentRound = round;

                    notifyKouDiStart();
                    notifyRoundStart();
                    log.info("[GameSetCore] 抠底流程开始，等待庄家扣底...");
                }

                if (this.state == SetState.KOU_DI_STATE && isDipaiEnd(sec)) {
                    autoKouDiIfNeeded();

                    roundEndWaitUntil = System.currentTimeMillis() / 1000 + gameRoomProperties.getRoundEndLagTime();
                    log.info("[GameSetCore] 抠底阶段结束，等待 {} 秒后进入喊打流程", gameRoomProperties.getRoundEndLagTime()/1000);
                    return true;
                }
                break;

            case HAN_DA_STATE:
                if (hanDaStartSec == -1) {
                    hanDaStartSec = sec;
                    round = new GameRoundCore(this, ++roundId);
                    roundPos = new GameRoundPos(getDealerSeatIndex());
                    roundPos.getReceiveOpTypes().clear();
                    roundPos.getReceiveOpTypes().add(PKOpType.HAN_DA);
                    roundPos.getReceiveOpTypes().add(PKOpType.DA_DU);
                    round.getRoundPosMap().put(roundPos.getSeatIndex(), roundPos);
                    this.currentRound = round;
                    notifyRoundStart(); // ✅ 通知庄家可以操作
                    notifyHandaWait();  // ✅ 广播所有人等待喊打阶段
                    log.info("[GameSetCore] 喊打流程开始，等待庄家喊打...");
                }

                // 判断喊打阶段是否结束
                if (this.state == SetState.HAN_DA_STATE && isHanDaEnd(sec)) {
                    log.info("[GameSetCore] 喊打阶段结束，进入出牌流程");
                    // 可在此处执行默认喊打逻辑（如未操作）
                    autoHanDaIfNeeded();
                    roundEndWaitUntil = System.currentTimeMillis() / 1000 + gameRoomProperties.getRoundEndLagTime();
                    log.info("[GameSetCore] 喊打阶段结束，等待 {} 秒后进入出牌流程", gameRoomProperties.getRoundEndLagTime()/1000);
                    return true;
                }
                break;

            case PLAYING_STATE:
                log.info("[GameSetCore] 出牌阶段update，等待玩家出牌...");

                if (!playStartNotified) {
                    int starterSeat = getLastWinnerOrDealer();
                    notifyPlayStart(roundId + 1, starterSeat);
                    playStartNotified = true;
                }

                if (currentRound == null) {
                    int starterSeat = getLastWinnerOrDealer();
                    log.info("[GameSetCore] 打牌，由 {} 开始出牌", starterSeat);

                    round = new GameRoundCore(this, ++roundId);
                    for (Integer seat : playerMap.keySet()) {
                        PKOpType opType = seat.equals(starterSeat) ? PKOpType.OUT_CARD : PKOpType.FOLLOW;
                        round.addWaitingPlayer(seat, opType);
                    }

                    this.currentRound = round;

                    notifyRoundStart();
                    log.info("[GameSetCore] 开启新出牌回合，由 {} 先出牌", starterSeat);

                    // 设置首出玩家的出牌时间
                    GameRoundPos starterPos = round.getRoundPosMap().get(starterSeat);
                    if (starterPos != null) {
                        starterPos.setOpStartTime(System.currentTimeMillis() / 1000);
                    }
                }

                boolean isRoundClosed = currentRound.update(sec);

                break;

            case ENDED_STATE:
                clearSetData();
                return true;
        }
        return false;
    }

    public int getLastWinnerOrDealer() {
        return latestWinnerSeat != -1
                ? latestWinnerSeat
                : getDealerSeatIndex() != -1 ? getDealerSeatIndex() : 0;
    }

    public void clearSetData() {
        log.info("[GameSetCore:{}] 清理对局数据", setId);
        playerMap.clear();
        cardDeck.clear();
        remainningDeck.clear();
        currentRound = null;
        kouPaiList.clear();
        hanDaCard = -1;
        oneVsFour = false;
        alreadyOpSeatSet.clear();
        teamMap.clear();
        handaPos = -1;
        liangZhuInfo = null;
        isFanzhu = false;
        baseScoreMultiplier = 1;
        curK510Point = 0;
        shengjiCount = 0;
        dipaiStartSec = -1;
        hanDaStartSec = -1;
        log.info("[GameSetCore:{}] 清理完成", setId);
    }

    private boolean isDipaiEnd(int sec) {
        return dipaiStartSec > 0 && (sec - dipaiStartSec >= gameRoomProperties.getKouDiTimeLimit()); // 15秒思考时间
    }

    private void autoKouDiIfNeeded() {
        GameRoundPos dealerRoundPos = currentRound.getRoundPosMap().get(getDealerSeatIndex());
        this.currentRound.setCurrentTurnSeat(getDealerSeatIndex());

        if (!dealerRoundPos.isOperated()) {
            log.info("[GameSetCore:{}] 庄家 seat={} 超时未扣底，执行默认扣底逻辑", setId, getDealerSeatIndex());

            doKouDi();
        }
    }

    private void autoHanDaIfNeeded() {
        // 如果庄家尚未进行喊打操作，执行默认喊打
        GameRoundPos dealerRoundPos = currentRound.getRoundPosMap().get(getDealerSeatIndex());
        this.currentRound.setCurrentTurnSeat(getDealerSeatIndex());
        if (!dealerRoundPos.isOperated()) {
            log.info("[GameSetCore:{}] 庄家 seat={} 超时未喊打，执行默认喊打逻辑", setId, getDealerSeatIndex());
            doHanda();
        }
    }

    private boolean isHanDaEnd(int sec) {
        // 假设喊打阶段时间为 60 秒
        return sec - hanDaStartSec >= gameRoomProperties.getHandaTimeLimit();
    }

    public void notifyScoreMultiplier() {
        int baseScore = 1;
        int daduBase = isOneVsFour() ? 2 : 1;
        int shengjiMultiplier= -1;

        if (curK510Point == 0 || curK510Point > 200) {
            shengjiMultiplier = -1; // 特殊标识：代表 base×20
        } else {
            shengjiMultiplier = 1 + shengjiCount;
        }
        WsPacket<ScoreMultiplierPush> packet = WsPackets.scoreMultiplier(baseScore, daduBase, baseScoreMultiplier, shengjiMultiplier, isZhuangWin());
        socketHandler.broadcastPacketToRoom(room.getRoomId(), packet);
        log.info("[GameSetCore:{}] 广播分数加倍状态封包：{}", setId, packet.getData());
    }

    public void notifyScoreMultiplierIfChanged() {
        int baseScore = 1;
        int daduBase = isOneVsFour() ? 2 : 1;
        int shengjiMultiplier;

        if (curK510Point == 0 || curK510Point > 200) {
            shengjiMultiplier = -1;
        } else {
            shengjiMultiplier = 1 + shengjiCount;
        }

        if (shengjiMultiplier != lastPushedShengjiMultiplier) {
            lastPushedShengjiMultiplier = shengjiMultiplier;

            WsPacket<ScoreMultiplierPush> packet = WsPackets.scoreMultiplier(
                    baseScore,
                    daduBase,
                    baseScoreMultiplier,
                    shengjiMultiplier,
                    isZhuangWin()
            );
            socketHandler.broadcastPacketToRoom(room.getRoomId(), packet);
            log.info("[GameSetCore:{}] 广播分数加倍状态变更封包：{}", setId, packet.getData());
        }
    }

    public void endSet() {
        if (this.state == SetState.ENDED_STATE) {
            log.info("[GameSetCore:{}] 本局已经结束", setId);
            return;
        }

        log.info("[GameSetCore:{}] 本局结束", setId);
        this.state = SetState.ENDED_STATE;
        this.playStartNotified = false;

        try {
            doFanDi();
        } catch (Exception e) {
            log.error("[GameSetCore:{}] 执行翻底异常", setId, e);
            broadcastError(GameErrorCode.FANDI_ERROR, "翻底失败");
        }

        try {
            calcShengJiPoint();
            notifyScoreMultiplier();
        } catch (Exception e) {
            log.error("[GameSetCore:{}] 计算升级点数异常", setId, e);
            broadcastError(GameErrorCode.CALC_SHENGJI_ERROR, "升级点数计算失败");
        }

        try {
            calcScore();
        } catch (Exception e) {
            log.error("[GameSetCore:{}] 计算得分异常", setId, e);
            broadcastError(GameErrorCode.CALC_SCORE_ERROR, "结算得分失败");
        }

        try {
            // 推送结算信息
            List<SetEndPush.SetResult> resultList = playerMap.entrySet().stream()
                    .map(entry -> {
                        int seat = entry.getKey();
                        GameSetPos pos = entry.getValue();
                        return new SetEndPush.SetResult(seat, pos.getScore());
                    })
                    .collect(Collectors.toList());

            log.info("[GameSetCore:{}] 推送结算消息 {}", setId, resultList);

            int bigSeat = currentRound.getWinnerSeat();     // 获胜座位
            int bigTeamId = getTeamId(bigSeat);             // 获胜队伍
            int setScore = curK510Point;                    // 当前总得分（含翻底）
            int fandiScoreFinal = fandiScore;               // 翻底得分

            WsPacket<SetEndPush> packet = WsPackets.setEnd(setId, bigSeat, bigTeamId, setScore, fandiScoreFinal, resultList);
            socketHandler.broadcastPacketToRoom(room.getRoomId(), packet);
        } catch (Exception e) {
            log.error("[GameSetCore:{}] 推送结算消息异常", setId, e);
            broadcastError(GameErrorCode.PUSH_SETTLE_ERROR, "推送结算失败");
        }

        for (GameRoomPos pos : room.getSeats()) {
            if (pos.isEmpty()) {
                continue;
            }
            // 玩家设置非准备状态
            pos.setReady(false);
        }


        try {
            saveGameSet();
        } catch (Exception e) {
            log.error("[GameSetCore:{}] 保存对局异常", setId, e);
            broadcastError(GameErrorCode.SAVE_GAMESET_ERROR, "保存牌局失败");
        }

        try {
            settleRealPlayers();
        } catch (Exception e) {
            log.error("[GameSetCore:{}] 结算真人玩家异常", setId, e);
            broadcastError(GameErrorCode.SETTLE_PLAYER_ERROR, "玩家结算失败");
        }
    }

    /** 批量广播 error */
    private void broadcastError(int code, String msg) {
        for (GameRoomPos pos : room.getSeats()) {
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
            if (!pos.isEmpty() && session != null) {
                socketHandler.sendError(session, code, msg);
            }
        }
    }

    private void saveGameSet() {
        // ✅ 存储本局对局记录
        GameSet gameSet = new GameSet();
        gameSet.setId(setId); // 必须有id！
        gameSet.setDealerPos(dealerSeatIndex);
        endTime = new Date();
        gameSet.setEndTime(endTime);

        // 构建结算 JSON
        JSONArray resultArray = new JSONArray();
        for (Map.Entry<Integer, GameSetPos> entry : playerMap.entrySet()) {
            JSONObject playerResult = new JSONObject();
            playerResult.put("seat", entry.getKey());
            playerResult.put("playerId", entry.getValue().getPlayerId());
            playerResult.put("score", entry.getValue().getScore());
            resultArray.add(playerResult);
        }
        gameSet.setResultJson(resultArray.toJSONString());

        // 保存
        gameSetService.updateGameSetResult(gameSet);
        log.info("[GameSetCore:{}] 对局结果已写入数据库", setId);
    }

    private void settleRealPlayers() {
        log.info("[GameSetCore:{}] 开始结算真人玩家...", setId);

        for (GameRoomPos pos : room.getSeats()) {
            if (pos.isEmpty()) {
                continue;
            }
            if (pos.isRobot()) {
                log.info("[GameSetCore:{}] 座位 {} 是机器人，跳过结算", setId, pos.getSeatIndex());
                continue;
            }

            Long playerId = pos.getPlayerId();
            if (playerId == null || playerId == 0) {
                log.warn("[GameSetCore:{}] 座位 {} 无效玩家ID，跳过", setId, pos.getSeatIndex());
                continue;
            }

            try {
                // 查询玩家信息
                GamePlayer player = gamePlayerService.selectGamePlayerById(playerId);
                if (player == null) {
                    log.warn("[GameSetCore:{}] 玩家ID={} 不存在，跳过结算", setId, playerId);
                    continue;
                }

                gamePlayerService.updateGamePlayer(player);

                log.info("[GameSetCore:{}] 玩家 {} 结算完成：房卡 {} -> {}", setId, playerId);

            } catch (Exception e) {
                log.error("[GameSetCore:{}] 结算玩家 {} 时异常", setId, playerId, e);
            }
        }

        log.info("[GameSetCore:{}] 结算真人玩家完成", setId);
    }

    private void doFanDi() {
        log.info("[GameSetCore:{}] 检查是否执行翻底", setId);
        if (isZhuangBig(currentRound.getWinnerSeat())) {
            log.info("[GameSetCore:{}] 非闲家胜出，不翻底", setId);
            return;
        }

        // 把底牌也记为吃分
        int point = kouPaiList.stream()
                .map(BasePoker::getCardValue)
                .mapToInt(v -> {
                    if (v == 5) return 5;
                    if (v == 10 || v == 13) return 10;
                    return 0;
                })
                .sum();

        this.fandiScore = point * 2;
        this.curK510Point += fandiScore;
        log.info("[GameSetCore:{}] 翻底完成，底牌得分加成，当前加倍={}", setId, baseScoreMultiplier);
    }

    private void calcShengJiPoint() {
        log.info("[GameSetCore:{}] 计算升级层级，510K得分={}", setId, curK510Point);
        boolean allOut = playerMap.values().stream().allMatch(p -> p.getHandCards().isEmpty());
        if (!allOut) {
            shengjiCount = 0;
            return;
        }

        if (curK510Point == 0) {
            shengjiCount = 3;
        } else if (curK510Point < 40 || curK510Point >= 160) {
            shengjiCount = 2;
        } else if (curK510Point >= 120 || curK510Point < 80) {
            shengjiCount = 1;
        } else if (curK510Point > 200) {
            shengjiCount = 2 + (curK510Point - 200) / 40;
        } else {
            shengjiCount = 0;
        }

        log.info("[GameSetCore:{}] 计算升级完成，升{}级", setId, shengjiCount);
    }

    /**
     * 获取指定队伍所有成员的 seatIndex 列表
     *
     * @param teamId 队伍编号（1 或 2）
     * @return 座位索引集合
     */
    private List<Integer> getTeamSeats(int teamId) {
        Set<Integer> teamSet = teamMap.getOrDefault(teamId, Collections.emptySet());
        return new ArrayList<>(teamSet);
    }

    private void calcScore() {
        int dealerTeamId = getTeamId(dealerSeatIndex);
        int winTeamId = isZhuangWin() ? dealerTeamId : (dealerTeamId == 1 ? 2 : 1);
        int loseTeamId = (winTeamId == 1 ? 2 : 1);

        List<Integer> winners = getTeamSeats(winTeamId);
        List<Integer> losers = getTeamSeats(loseTeamId);

        log.info("[GameSetCore:{}] 本局胜利方：队伍 {}，胜方座位 = {}", setId, winTeamId, winners);
        log.info("[GameSetCore:{}] 本局失败方：队伍 {}，败方座位 = {}", setId, loseTeamId, losers);

        if (winners.isEmpty() || losers.isEmpty()) {
            log.warn("[GameSetCore:{}] 胜负方队伍异常，跳过计分", setId);
            return;
        }

        int peopleCount = Math.max(winners.size(), losers.size());
        BigDecimal baseScore = BigDecimal.valueOf(mapPointToScore());
        BigDecimal totalScore = baseScore.multiply(BigDecimal.valueOf(peopleCount));

        log.info("[GameSetCore:{}] baseScore = {}，总分 = {}（按人数 {} 计算）", setId, baseScore, totalScore, peopleCount);

        Map<Integer, BigDecimal> winShareMap = new HashMap<>();
        Map<Integer, BigDecimal> loseShareMap = new HashMap<>();

        // 胜方分配
        if (winners.contains(dealerSeatIndex)) {
            int otherCount = winners.size() - 1;
            int totalShares = 2 + otherCount;
            BigDecimal oneShare = totalScore.divide(BigDecimal.valueOf(totalShares), RoundingMode.DOWN);

            for (Integer seat : winners) {
                BigDecimal score = seat == dealerSeatIndex ? oneShare.multiply(BigDecimal.valueOf(2)) : oneShare;
                winShareMap.put(seat, score);
                log.info("[GameSetCore:{}] 胜方 seat={} 得分 = {}", setId, seat, score);
            }
        } else {
            BigDecimal perShare = totalScore.divide(BigDecimal.valueOf(winners.size()), RoundingMode.DOWN);
            for (Integer seat : winners) {
                winShareMap.put(seat, perShare);
                log.info("[GameSetCore:{}] 胜方 seat={} 得分 = {}", setId, seat, perShare);
            }
        }

        // 败方分配
        if (losers.contains(dealerSeatIndex)) {
            int otherCount = losers.size() - 1;
            int totalShares = 2 + otherCount;
            BigDecimal oneShare = totalScore.divide(BigDecimal.valueOf(totalShares), RoundingMode.DOWN);

            for (Integer seat : losers) {
                BigDecimal score = seat == dealerSeatIndex ? oneShare.multiply(BigDecimal.valueOf(2)) : oneShare;
                loseShareMap.put(seat, score);
                log.info("[GameSetCore:{}] 败方 seat={} 失分 = {}", setId, seat, score);
            }
        } else {
            BigDecimal perShare = totalScore.divide(BigDecimal.valueOf(losers.size()), RoundingMode.DOWN);
            for (Integer seat : losers) {
                loseShareMap.put(seat, perShare);
                log.info("[GameSetCore:{}] 败方 seat={} 失分 = {}", setId, seat, perShare);
            }
        }

        Map<Integer, BigDecimal> totalDelta = new HashMap<>(); // seatIndex -> 最终得分（可能为负）

        // 初始化每个玩家的得分变动为0
        for (Integer seat : playerMap.keySet()) {
            totalDelta.put(seat, BigDecimal.ZERO);
        }

        // 胜方总分，用于计算占比
        BigDecimal totalWinAmount = winShareMap.values().stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // ❗先用浮点精度分配分数变动（不调用 addScore）
        for (Integer loserSeat : losers) {
            BigDecimal loseAmount = loseShareMap.get(loserSeat);

            for (Map.Entry<Integer, BigDecimal> winEntry : winShareMap.entrySet()) {
                int winnerSeat = winEntry.getKey();
                BigDecimal winShare = winEntry.getValue();

                // 比例转移，保持浮点精度
                BigDecimal transfer = loseAmount.multiply(winShare).divide(totalWinAmount, 10, RoundingMode.HALF_UP);

                totalDelta.put(loserSeat, totalDelta.get(loserSeat).subtract(transfer));
                totalDelta.put(winnerSeat, totalDelta.get(winnerSeat).add(transfer));
            }
        }

        // ✅ 最后再统一四舍五入，并调用 addScore
        for (Map.Entry<Integer, BigDecimal> entry : totalDelta.entrySet()) {
            int seat = entry.getKey();
            int score = entry.getValue().setScale(0, RoundingMode.HALF_UP).intValue();

            if (score == 0) continue;

            if (score > 0) {
                addScore(score, -1, seat); // 赢的分，从“系统”转入
            } else {
                addScore(-score, seat, -1); // 输的分，从 seat 转出到“系统”
            }
        }
    }

    private void addScore(int score, int loseSeat, int winSeat) {
        if (loseSeat != -1) {
            GameSetPos loser = playerMap.get(loseSeat);
            loser.setScore(loser.getScore() - score);
            GameRoomPos loserRoomPos = room.getSeats().get(loseSeat);
            if (loserRoomPos != null) {
                loserRoomPos.setScore(loserRoomPos.getScore() - score);
            }
        }

        if (winSeat != -1) {
            GameSetPos winner = playerMap.get(winSeat);
            winner.setScore(winner.getScore() + score);
            GameRoomPos winnerRoomPos = room.getSeats().get(winSeat);
            if (winnerRoomPos != null) {
                winnerRoomPos.setScore(winnerRoomPos.getScore() + score);
            }
        }

        log.info("[GameSetCore:{}] 分数转移：loserSeat={} -> winnerSeat={}，分数={}",
                setId, loseSeat, winSeat, score);
    }

    private int mapPointToScore() {
        // 大胜或大败情况：curK510Point == 0 或 > 200，固定分数为 20
        if (curK510Point == 0 || curK510Point > 200) {
            log.info("[GameSetCore:{}] 本局为大胜或大败（510K = {}），得分设为 20", setId, curK510Point);
            return 20;
        }

        // 正常情况：使用升级点数 × 底分倍数 × 是否1v4
        int base = 1+shengjiCount; // 使用 calcShengJiPoint 得到的升几级数值
        int daduBase = isOneVsFour()? 2 : 1;        // 打独的base
        int score = base * baseScoreMultiplier * daduBase;

        log.info("[GameSetCore:{}] 正常结算：升级级数={}，底分倍数={}，打独倍数={}, 最终得分={}",
                setId, base, baseScoreMultiplier, daduBase, score);
        return score;
    }

    private void notifyPlayStart(int roundId, int starterSeat) {
        WsPacket<?> packet = WsPackets.playStart(roundId, starterSeat);
        for (GameRoomPos pos : room.getSeats()) {
            if (pos.isEmpty()) continue;
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
            if (session != null && session.isOpen()) {
                socketHandler.sendPacketToSession(session, packet);
            }
        }
        log.info("[GameSetCore] 广播打牌阶段开始：roundId={}, 首出seat={}", roundId, starterSeat);
    }

    private void notifyCallZhuEnd() {
        int dealerSeatIndex = getDealerSeatIndex();
        int zhuColor = liangZhuInfo.getZhuColor();
        int zhuValue = getZhuValue();

        log.info("[GameSetCore] 推送 CALL_ZHU_END：庄家={}, 主花色={}, 主点数={}", dealerSeatIndex, zhuColor, zhuValue);

        for (GameRoomPos roomPos : room.getSeats()) {
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
            if (session != null && session.isOpen()) {
                socketHandler.sendPacketToSession(session, WsPackets.callZhuEnd(dealerSeatIndex, zhuColor, zhuValue));
            }
        }
    }

    private void notifyRoundStart() {
        if (this.currentRound == null) return;

        for (GameRoundPos roundPos : currentRound.getRoundPosMap().values()) {
            int seatIndex = roundPos.getSeatIndex();
            GameRoomPos roomPos = room.getSeats().get(seatIndex);
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
            log.info("[GameSetCore] 推送回合开始：roundId={}, seat={}, opTypes={}",
                    currentRound.getRoundId(), seatIndex, roundPos.getReceiveOpTypes());

            if (session != null && session.isOpen()) {
                Set<PKOpTypeReq> opTypesForClient = roundPos.getReceiveOpTypes().stream()
                        .map(this::mapToReq)
                        .collect(Collectors.toSet());

                // ✅ 1. 推送 START_ROUND：包含可选操作类型
                socketHandler.sendPacketToSession(session, WsPackets.startRound(
                        currentRound.getRoundId(),
                        seatIndex,
                        opTypesForClient
                ));

                if (latestWinnerSeat != -1) {
                    currentRound.setCurrentTurnSeat(latestWinnerSeat);
                }
                else if (dealerSeatIndex != -1) {
                    currentRound.setCurrentTurnSeat(dealerSeatIndex);
                }

                List<PlayerStateInfo> seatStates = new ArrayList<>();
                for (int seat = 0; seat < room.getSeats().size(); seat++) {
                    GameRoomPos rPos = room.getSeats().get(seat);
                    PlayerStateInfo info = new PlayerStateInfo();
                    info.setSeatIndex(seat);
                    info.setOnline(GameWebSocketHandler.isPlayerOnlineInRoom(rPos.getPlayerId(), getRoom().getRoomId()) || rPos.isRobot());
                    info.setRobot(rPos.isRobot());
                    info.setReady(rPos.isReady());
                    info.setPlayerName(rPos.getName());
                    seatStates.add(info);
                }

                // ✅ 2. 若当前是该玩家轮到出牌，推送 NEXT_PLAYER_OP
                if (seatIndex == currentRound.getCurrentTurnSeat()) {
                    Integer nextNextSeat = currentRound.findNextSeat(seatIndex);
                    if (state == SetState.PLAYING_STATE) {
                        // 只有玩牌阶段会推送NEXT_PLAYER_OP
                        socketHandler.broadcastPacketToRoom(
                                getRoom().getRoomId(),
                                WsPackets.nextPlayerOp(seatIndex, nextNextSeat, seatStates)
                        );log.info("[GameSetCore] 向 seat={} 推送 NEXT_PLAYER_OP 出牌提醒", seatIndex);
                    }
                }
            }
        }
    }

    private PKOpTypeReq mapToReq(PKOpType opType) {
        return switch (opType) {
            case OUT_CARD, FOLLOW -> PKOpTypeReq.OUT_CARD;
            case LIANG_ZHU1, LIANG_ZHU2, FAN_ZHU, ZI_BAO, ZI_FAN -> PKOpTypeReq.LIANG_ZHU;
            case KOU_DI -> PKOpTypeReq.KOU_DI;
            case DA_DU -> PKOpTypeReq.DA_DU;
            case HAN_DA -> PKOpTypeReq.HAN_DA;
            default -> PKOpTypeReq.NONE;
        };
    }

    public int getDealerPos() {
        return dealerSeatIndex;
    }

    public boolean isAllCardsPlayed() {
        boolean allEmpty = true;

        for (Map.Entry<Integer, GameSetPos> entry : playerMap.entrySet()) {
            int seat = entry.getKey();
            GameSetPos pos = entry.getValue();
            List<Integer> hand = pos.getHandCards();

            if (hand.isEmpty()) {
                log.info("[SetCheck] 玩家 seat={} playerId={} 手牌已出完", seat, pos.getPlayerId());
            } else {
                log.info("[SetCheck] 玩家 seat={} playerId={} 剩余手牌：{}", seat, pos.getPlayerId(), BasePoker.toCardString(hand));
                allEmpty = false;
            }
        }

        return allEmpty;
    }

    private boolean isGrabZhuangEnd(int sec) {
        long nowSec = System.currentTimeMillis() / 1000;
        if (nowSec - grabStartTime > gameRoomProperties.getCallzhuTimeLimit()) {
            log.info("[GameSetCore] 抢庄阶段时间到，自动结束");
            return true;
        }
//        if (alreadyOpSeatSet.size() == playerMap.size()) {
//            log.info("[GameSetCore] 所有玩家已操作完抢庄，自动进入下阶段");
//            return true;
//        }
        return false;
    }

    private boolean anyoneCalledMaster() {
        return liangZhuInfo != null && liangZhuInfo.getZhuColor() != -1;
    }

    private void dealDiPai() {
        // 获取底牌，避免并发修改异常
        List<Integer> diPais = kouPaiList;
        log.info("[GameSetCore] 摸老底底牌列表：{}", toCardString(diPais));

        // 无人亮主，进行“摸老底”
        if (liangZhuInfo == null ||liangZhuInfo.getZhuColor() == -1) {
            // 如果 liangZhuInfo 为空，则需要新建一个对象
            if (liangZhuInfo == null) {
                liangZhuInfo = new LiangZhuInfo(-1, PKOpType.LIANG_ZHU1, new ArrayList<>(), zhuValue, -1);
            }

            int laoDiCard = diPais.get(0);
            liangZhuInfo.setZhuColor(BasePoker.getCardColor(laoDiCard));
            int count = getCountValue(laoDiCard);

            int baseline = (roundId == 0)
                    ? new Random().nextInt(playerMap.size())
                    : dealerSeatIndex;
            int newDealer = (baseline + (count - 1)) % playerMap.size();

            dealerSeatIndex = newDealer;
            log.info("[GameRoomSet] 摸老底确定主花色为 {}，庄家设为 {}", liangZhuInfo.getZhuColor(), newDealer);
        }

        currentRound.setCurrentTurnSeat(dealerSeatIndex);
        // 将底牌发给庄家
        GameSetPos dealer = playerMap.get(dealerSeatIndex);
        if (dealer == null) {
            log.error("[GameSetCore] 找不到庄家，无法发底牌");
            room.getSeats().forEach(pos -> {
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                if (!pos.isEmpty() && session != null) {
                    socketHandler.sendError(session, GameErrorCode.DEALER_NOT_FOUND, "发底牌失败，庄家缺失");
                }
            });
            return;
        }

        dealer.getHandCards().addAll(diPais);
        log.info("[GameSetCore] 发底牌给庄家 {}: {}", dealerSeatIndex, diPais);
    }

    private int getCountValue(int card) {
        int value = BasePoker.getCardValue(card);
        return BasePoker.isJoker(card) ? 1 : value;
    }

    public boolean handlePlayerOp(int seatIndex, int roundId, PKOpType opType, List<Integer> cards) {
        log.info("[GameSetCore] 当前回合 = {}, 当前 roundId = {}", currentRound != null ? currentRound.getRoundId() : null, roundId);
        GameSetPos setPos = playerMap.get(seatIndex);
        if (setPos == null) {
            GameRoomPos roomPos = room.getSeats().get(seatIndex);
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
            if (roomPos != null && session != null) {
                socketHandler.sendError(session, GameErrorCode.PLAYER_NOT_FOUND, "玩家未找到");
            }
            return false;
        }
        try {
            if (!canOperate(seatIndex, opType)) {
                GameRoomPos roomPos = room.getSeats().get(seatIndex);
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
                if (roomPos != null && session != null) {
                    socketHandler.sendError(session, GameErrorCode.INVALID_OPTYPE, "当前状态不允许此操作");
                }
                return false;
            }

            boolean success = setPos.doOpType(opType, cards, this, false);
            if (!success)
                return false;

            if (opType.isLiangZhuOp()) {
                alreadyOpSeatSet.add(seatIndex);
            }

            // ✅ 广播操作
            boolean isBig = true;
            if (opType == PKOpType.FOLLOW && getLatestWinnerSeat() != seatIndex) {
                isBig = false;
            }
            // ✅ 判断是否是喊打帮手
            int handaCardValue = (handaPos == seatIndex) ? hanDaCard : -1;
            socketHandler.broadcastPacketToRoom(
                    room.getRoomId(),
                    WsPackets.actionBroadcast(seatIndex, mapToReq(opType), cards, isBig, handaCardValue)
            );

            log.info("[GameSetCore:{}] 座位 {} 出牌成功", setId, seatIndex);

            currentRound.recordOp(seatIndex);
            if (currentRound.allPlayerOperated()) {
                log.info("[GameSetCore:{}] 本轮全部出牌完成，设置延迟结算时间", setId);
                // 设置等待时间戳（单位秒）
                this.roundEndWaitUntil = System.currentTimeMillis() / 1000 + gameRoomProperties.getRoundEndLagTime();
            }
            return true;
        } catch (GameOpException e) {
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(setPos.getPlayerId());
            if (setPos != null && session != null) {
                socketHandler.sendError(session, e.getErrorCode(), e.getMessage());
            }
            return false;
        }
    }

    /**
     * 托管处理：在当前状态下由机器人或托管玩家执行操作（调用统一 doOpType）
     */
    public boolean handleTrusteeship(int seatIndex) {
        GameSetPos pos = playerMap.get(seatIndex);
        if (pos == null) {
            log.warn("[GameSetCore:{}] seatIndex={} 无对应玩家，跳过托管", setId, seatIndex);
            GameRoomPos roomPos = room.getSeats().get(seatIndex);
            WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(roomPos.getPlayerId());
            if (roomPos != null && session != null) {
                socketHandler.sendError(session, GameErrorCode.PLAYER_NOT_FOUND, "玩家不存在");
            }
            return false;
        }

        if (currentRound == null) {
            log.info("[GameSetCore:{}] 当前没有激活的回合，跳过托管", setId);
            return false;
        }

        GameRoundPos roundPos = currentRound.getRoundPosMap().get(seatIndex);
        if (roundPos == null) {
            log.info("[GameSetCore:{}] 当前回合未轮到 seat={} 操作", setId, seatIndex);
            return false;
        }

        Set<PKOpType> ops = roundPos.getReceiveOpTypes();
        if (ops == null || ops.isEmpty()) {
            log.info("[GameSetCore:{}] seat={} 没有可执行操作", setId, seatIndex);
            return false;
        }
        log.info("[GameSetCore:{}] 托管处理 seatIndex={}, 当前状态={}", setId, seatIndex, state);

        // 排序操作，按照 FAN_ZHU, LIANG_ZHU2, ZI_FAN, ZI_BAO, LIANG_ZHU1 的优先级执行
        List<PKOpType> sortedOps = ops.stream()
                .sorted(Comparator.comparingInt(opType -> getOpPriority(opType)))  // 自定义排序逻辑
                .collect(Collectors.toList());

        for (PKOpType opType : sortedOps) {
            try {
                if (canOperate(seatIndex, opType)) {
                    List<Integer> cards = pos.tryAutoCards(opType, this);
                    boolean ok = pos.doOpType(opType, cards, this, true);
                    if (ok) {
                        if (opType.isLiangZhuOp()) {
                            alreadyOpSeatSet.add(seatIndex);
                        }

                        boolean isBig = true;
                        if (opType == PKOpType.FOLLOW) {
                            // 跟牌需要获取比较值
                            if (getLatestWinnerSeat() != seatIndex) {
                                // 胜利者不是自己
                                isBig = false;
                            }
                        }
                        // ✅ 判断是否是喊打帮手
                        int handaCardValue = (handaPos == seatIndex) ? hanDaCard : -1;

                        socketHandler.broadcastPacketToRoom(room.getRoomId(), WsPackets.actionBroadcast(pos.getSeatIndex(), mapToReq(opType), cards, isBig, handaCardValue));
                        log.info("[GameSetCore] seat={} 执行 {} 托管操作，成功={}", seatIndex, opType, true);

                        currentRound.recordOp(seatIndex);
                        if (currentRound.allPlayerOperated()) {
                            this.roundEndWaitUntil = System.currentTimeMillis() / 1000 + gameRoomProperties.getRoundEndLagTime();
                            log.info("[GameSetCore:{}] 本轮全部出牌完成，设置延迟 {} 秒后结算", setId, gameRoomProperties.getRoundEndLagTime());
                        }

                        return true; // ✅ 有成功操作
                    }
                }
            } catch (GameOpException e) {
//                log.warn("[GameSetCore:{}] 托管 seat={} 执行操作异常: {}", setId, seatIndex, e.getMessage());
            }
        }

        // 所有操作都失败，返回 false，要求外部暂停推进
        log.warn("[GameSetCore:{}] seat={} 所有托管操作失败", setId, seatIndex);
        return false;
    }

    public int addRoundScore(int winnerSeat) {
        GameSetPos winner = playerMap.get(winnerSeat);
        if (winner == null) return 0;

        // ✅ 当前回合所有出牌
        List<Integer> cards = currentRound != null ? currentRound.getRoundCards() : Collections.emptyList();
        if (cards.isEmpty()) return 0;

        // 加分逻辑（每张牌 5 / 10 / K 有固定分数）
        int point = cards.stream()
                .map(BasePoker::getCardValue)
                .mapToInt(v -> {
                    if (v == 5) return 5;
                    if (v == 10 || v == 13) return 10;
                    return 0;
                })
                .sum();

        if (point > 0) {
            boolean zhuangWin = isZhuangBig(winnerSeat);
            log.info("[GameSetCore:{}] 回合{} 赢家={}，胜方为{}家", setId, currentRound.getRoundId(), winnerSeat, zhuangWin ? "庄" : "闲");

            if (!zhuangWin) {
                curK510Point += point;
                log.info("[GameSetCore:{}] 闲家吃分 {}，累计得分：{}", setId, point, curK510Point);
                return point;
            } else {
                log.info("[GameSetCore:{}] 庄家吃分 {}（不计入总得分）", setId, point);
            }
        }
        else {
            log.info("[GameSetCore:{}] 回合{} 赢家={}，无吃分", setId, currentRound.getRoundId(), winnerSeat);
        }
        log.info("[GameSetCore] 本轮出牌：{}", BasePoker.toCardString(cards));
        return 0;
    }

    public boolean isZhuangBig(int winnerSeat) {
        Set<Integer> zhuangTeam = teamMap.get(1); // 1 号队伍默认为庄家队伍
        return zhuangTeam != null && zhuangTeam.contains(winnerSeat);
    }

    public boolean canOperate(int seatIndex, PKOpType opType) {
        GameRoundCore round = this.getCurrentRound();

        if (round == null) {
            log.info("[GameSetCore:{}] 拒绝操作：当前没有有效回合，seat={} opType={}", setId, seatIndex, opType);
            throw new GameOpException(GameErrorCode.INVALID_OP, "拒绝操作：当前没有有效回合");
        }

        // seatIndex 是否轮到操作
        if (round.getCurrentTurnSeat() != -1 && round.getCurrentTurnSeat() != seatIndex) {
            log.info("[GameSetCore:{}] 拒绝操作：当前轮到 seat={} 操作，seatIndex={} 无权操作", setId, round.getCurrentTurnSeat(), seatIndex);
            throw new GameOpException(GameErrorCode.INVALID_OP, "拒绝操作：当前没有轮到你操作");
        }

        // seatIndex 是否已经操作过了
        GameRoundPos roundPos = round.getRoundPosMap().get(seatIndex);
        if (state != SetState.CALL_ZHU_STATE && roundPos != null && roundPos.isOperated()) {
            log.info("[GameSetCore:{}] seat={} 已经操作过，跳过", setId, seatIndex);
            throw new GameOpException(GameErrorCode.INVALID_OP, "拒绝操作：你已经操作过了");
        }

        switch (state) {
            case CALL_ZHU_STATE -> {
                boolean result = opType.isLiangZhuOp();
                if (!result) {
                    throw new GameOpException(GameErrorCode.INVALID_OP, "亮主阶段必须做亮主相关操作");
                }
                log.info("[GameSetCore:{}] 状态=CALL_ZHU_STATE, seat={}, opType={}, isLiangZhuOp={}, 结果={}",
                        setId, seatIndex, opType, opType.isLiangZhuOp(), result);
                return true;
            }
            case KOU_DI_STATE -> {
                boolean result = opType == PKOpType.KOU_DI && seatIndex == dealerSeatIndex && round != null;
                if (!result) {
                    throw new GameOpException(GameErrorCode.INVALID_OP, "扣底阶段必须做扣底操作");
                }
                log.info("[GameSetCore:{}] 状态=KOU_DI_STATE, seat={}, opType={}, isDealer={}, round!=null={}, 结果={}",
                        setId, seatIndex, opType, seatIndex == dealerSeatIndex, round != null, result);
                return true;
            }
            case HAN_DA_STATE -> {
                boolean result = (opType == PKOpType.HAN_DA || opType == PKOpType.DA_DU)
                        && seatIndex == dealerSeatIndex && round != null;
                if (!result) {
                    throw new GameOpException(GameErrorCode.INVALID_OP, "喊打阶段必须做喊打或者打独操作");
                }
                log.info("[GameSetCore:{}] 状态=HAN_DA_STATE, seat={}, opType={}, isDealer={}, round!=null={}, 结果={}",
                        setId, seatIndex, opType, seatIndex == dealerSeatIndex, round != null, result);
                return true;
            }
            case PLAYING_STATE -> {
                boolean result = (opType == PKOpType.OUT_CARD || opType == PKOpType.FOLLOW) && round != null;
                if (!result) {
                    throw new GameOpException(GameErrorCode.INVALID_OP, "打牌阶段必须做打牌或者跟牌相关操作");
                }
                log.info("[GameSetCore:{}] 状态=PLAYING_STATE, seat={}, opType={}, round!=null={}, 结果={}",
                        setId, seatIndex, opType, round != null, result);
                return true;
            }
            default -> {
                log.info("[GameSetCore:{}] 状态={} 不允许任何操作，seat={}, opType={}", setId, state, seatIndex, opType);
                return false;
            }
        }
    }

    private int getOpPriority(PKOpType opType) {
        switch (opType) {
            case FAN_ZHU:
                return 1;
            case LIANG_ZHU2:
                return 2;
            case ZI_FAN:
                return 3;
            case ZI_BAO:
                return 4;
            case LIANG_ZHU1:
                return 5;
            case HAN_DA:
                return 6;
            case DA_DU:
                return 7;
            default:
                return Integer.MAX_VALUE;  // 其他操作的优先级最低
        }
    }

    /**
     * 根据座位索引获取对应的 GameSetPos
     */
    public GameSetPos getGameSetPosBySeatIndex(int seatIndex) {
        return playerMap.get(seatIndex);
    }

    /**
     * 由系统调用，执行默认扣底操作
     */
    public void doKouDi() {
        int dealerSeatIndex = getDealerSeatIndex();
        GameSetPos dealerPos = getGameSetPosBySeatIndex(dealerSeatIndex);
        log.info("[GameSetCore] 自动扣底操作 - 庄家位置为 seatIndex={}, dealerPos={}", dealerSeatIndex, dealerPos.getPlayerId());
        if (dealerPos == null) {
            log.error("[GameSetCore] 无法找到庄家位置，扣底失败");
            room.getSeats().forEach(pos -> {
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                if (!pos.isEmpty() && session != null) {
                    socketHandler.sendError(session, GameErrorCode.DEALER_NOT_FOUND, "扣底失败，找不到庄家");
                }
            });
            return;
        }

        List<Integer> kouDiCards = dealerPos.tryAutoCards(PKOpType.KOU_DI, this);
        if (kouDiCards == null || kouDiCards.size() != 8) {
            log.warn("[GameSetCore] 扣底失败，AI 未能正确选择8张牌，实际选择={}", kouDiCards);
            room.getSeats().forEach(pos -> {
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                if (!pos.isEmpty() && session != null) {
                    socketHandler.sendError(session, GameErrorCode.KOUDI_FAILED, "扣底失败：AI未能选择8张牌");
                }
            });
            return;
        }

        boolean success = dealerPos.doOpType(PKOpType.KOU_DI, kouDiCards, this, true);
        if (!success) {
            log.warn("[GameSetCore] 扣底操作失败");
            room.getSeats().forEach(pos -> {
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                if (!pos.isEmpty() && session != null) {
                    socketHandler.sendError(session, GameErrorCode.KOUDI_FAILED, "扣底失败");
                }
            });
        } else {
            log.info("[GameSetCore] 系统自动为庄家扣底成功");
        }
    }

    /**
     * 由系统调用，执行默认扣底操作
     */
    public void doHanda() {
        int dealerSeatIndex = getDealerSeatIndex();
        GameSetPos dealerPos = getGameSetPosBySeatIndex(dealerSeatIndex);
        if (dealerPos == null) {
            log.error("[GameSetCore] 无法找到庄家位置，喊打失败");
            room.getSeats().forEach(pos -> {
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                if (!pos.isEmpty() && session != null) {
                    socketHandler.sendError(session, GameErrorCode.DEALER_NOT_FOUND, "喊打失败，找不到庄家");
                }
            });
            return;
        }

        // 默认喊打逻辑为“打独”
        PKOpType opType = PKOpType.DA_DU;
        List<Integer> cards = Collections.emptyList();

        boolean success = dealerPos.doOpType(opType, cards, this, true);
        if (!success) {
            log.warn("[GameSetCore] 自动喊打执行失败，seat={}", dealerSeatIndex);
            room.getSeats().forEach(pos -> {
                WebSocketSession session = GameWebSocketHandler.getSessionFromPlayerId(pos.getPlayerId());
                if (!pos.isEmpty() && session != null) {
                    socketHandler.sendError(session, GameErrorCode.HANDA_FAILED, "系统喊打失败");
                }
            });
        } else {
            log.info("[GameSetCore] 系统自动为庄家执行喊打操作：{}", opType);
        }
    }

    /**
     * 是否已经有人亮主
     */
    public boolean hasLiangZhu() {
        return liangZhuInfo != null;
    }

    public ReconnectPush buildReconnectPush(long playerId) {
        ReconnectPush push = new ReconnectPush();
        push.setRoomId(room.getRoomId());
        push.setSetId(setId);
        push.setDealerPos(dealerSeatIndex);
        push.setSetState(state.ordinal());

        push.setKouPaiList(kouPaiList);
        push.setZhuCards(liangZhuInfo != null ? liangZhuInfo.getZhuCards() : null);
        // 所有人亮主记录
        Map<Integer, List<Integer>> allLiangZhu = new HashMap<>();
        for (GameSetPos pos : playerMap.values()) {
            if (pos.getLiangZhuInfo() != null) {
                allLiangZhu.put(pos.getSeatIndex(), new ArrayList<>(pos.getLiangZhuInfo().getZhuCards()));
            }
        }
        push.setAllLiangZhuCards(allLiangZhu);
        push.setLiangzhuSeatIndex(liangZhuInfo != null ? liangZhuInfo.getSeatIndex() : null);

        push.setDealerTeam(new ArrayList<>(teamMap.getOrDefault(1, Set.of())));
        push.setIdleTeam(new ArrayList<>(teamMap.getOrDefault(2, Set.of())));
        push.setHelperCard(hanDaCard > 0 ? hanDaCard : null);

        push.setKouDiFinished(!kouPaiList.isEmpty()); // 有底牌即代表扣底完成
        push.setHandaFinished(hanDaCard > 0 || handaPos >= 0); // 有喊打牌或喊打人即视为喊打完成

        if (currentRound != null) {
            push.setRoundId(currentRound.getRoundId());
            push.setWaitPos(currentRound.getCurrentTurnSeat());
            if (currentRound != null) {
                push.setRoundCardMap(currentRound.buildRoundCardMap());
            };
            push.setPrevRoundCards(new ArrayList<>(prevRoundCards));
            push.setRemainCards(currentRound.getRemainingCards());
            push.setBigSeat(currentRound.getWinnerSeat());
            push.setBigTeamId(getTeamId(currentRound.getWinnerSeat()));
            push.setRoundScore(curK510Point);
            push.setSetScore(curK510Point); // 如果你有区分，本局总分 vs 当前回合分可区分
        }

        // 当前玩家专属信息
        GameSetPos myPos = getSetPosByPlayerId(playerId);
        if (myPos != null) {
            push.setMySeatIndex(myPos.getSeatIndex());
            push.setMyHandCards(new ArrayList<>(myPos.getHandCards()));
            push.setMyPlayedCards(new ArrayList<>(myPos.getLastPlayed())); // 当前轮已出牌

            // ✅ 本人亮主牌
            if (myPos.getLiangZhuInfo() != null) {
                push.setMyLiangZhuCards(new ArrayList<>(myPos.getLiangZhuInfo().getZhuCards()));
            }
        }

        // 分数倍率
        push.setBaseScore(baseScoreMultiplier);
        push.setDaduMultiplier(oneVsFour ? 2 : 1);
        push.setFanzhuMultiplier(isFanzhu ? 2 : 1);
        push.setShengjiMultiplier(shengjiCount > 0 ? 1 + shengjiCount : -1);
        push.setFinalScoreMultiplier(calcFinalMultiplier());
        push.setZhuangWin(isZhuangWin());

        return push;
    }

    private Map<Integer, List<Integer>> buildHandCardsMap() {
        return playerMap.values().stream()
                .collect(Collectors.toMap(GameSetPos::getSeatIndex, GameSetPos::getHandCards));
    }

    private int calcFinalMultiplier() {
        if (shengjiCount == -1) return baseScoreMultiplier * 20;
        return baseScoreMultiplier * (oneVsFour ? 2 : 1) * (isFanzhu ? 2 : 1) * (1 + shengjiCount);
    }

    public GameSetPos getSetPosByPlayerId(Long playerId) {
        return playerMap.values().stream()
                .filter(pos -> pos.getPlayerId() == playerId)
                .findFirst()
                .orElse(null);
    }
}

